﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.ComponentModel;
using System.Windows.Threading;
namespace SockLib
{
    public class SocketModel:INotifyPropertyChanged 
    {
        public SocketModel()
        {;}

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="BufferSize">缓冲区大小</param>
        public SocketModel(int BufferSize)
        {
            ReceiveBuffer = new byte[BufferSize];
            ReceiveSAEA.SetBuffer(ReceiveBuffer, 0, ReceiveBuffer.Length);
            ReceiveSAEA.Completed += new EventHandler<SocketAsyncEventArgs>(ReceiveSAEA_Completed);
            ADDDevices();
        }

        public void DelegateInit( CloseClient CC, TransferDatas TD,Register RM, Logs.WriteLog WL)
        {
            CloseOne = CC;       //关闭自己客户端
            TransferData = TD;   //数据转发
            RegisterMe = RM;     //注册成功
            WriteLog = WL;
        }

       public  Logs.WriteLog WriteLog;

        List<Device.IDevice> Devices=new List<Device.IDevice>();
        /// <summary>
        /// <para>添加一个设备方法</para>
        /// <para>1、需要在Device文件夹下增加驱动文件，实现接口Idevice</para>
        /// <para>2、在此处New一个实例，并ADD进入Devices</para>
        /// </summary>
        private void ADDDevices()
        {
            //在此处New一个实例，并ADD进入Devices
            Devices.Add(new Device.Normal());     
            Devices.Add(new Device.SXGPRS());     
            Devices.Add(new Device.ModBus());
            Devices.Add(new Device.JSON());
            Devices.Add(new Device.JZDevice());
            Devices.Add(new Device.GPRS2JSON());
            Devices.Add(new Device.STANDMODBUS());
        }

        /// <summary>
        /// 注册成功后获得该设备使用的驱动文件,使用注册条件来选择使用哪个驱动文件
        /// </summary>
        private Device.IDevice _RegDevice;
        internal   Device.IDevice RegDevice
        {
            set
            {
                _RegDevice = value;
                DeviceType = _RegDevice.DeviceType();
            }
            get { return _RegDevice; }
        }



        private string _DeviceType = "没有设备";
        /// <summary>
        /// 设备型号
        /// </summary>
        public string DeviceType
        {
            get
            {
                return _DeviceType;
            }
            set
            {
                _DeviceType = value;
                NotifyPropertyChanges("DeviceType");
            }
        }


        /// <summary>
        /// 接收数据使用的SocketAsyncEventArgs对象
        /// </summary>
        public SocketAsyncEventArgs ReceiveSAEA = new SocketAsyncEventArgs();



        /// <summary>
        /// 发送数据使用的SocketAsyncEventArgs
        /// </summary>
        /// <param name="Send">缓冲区数组</param>
        /// <returns>返回用于发送数据的SocketAsyncEventArgs</returns>
        public SocketAsyncEventArgs SendSAEA(byte[] Send)
        {
            SocketAsyncEventArgs SAEA = new SocketAsyncEventArgs();
            SAEA.SetBuffer(Send, 0, Send.Length );
            SAEA.AcceptSocket  = ReceiveSAEA.AcceptSocket ;
            SAEA.Completed += SAEA_Completed;
            return SAEA;
        }


        /// <summary>
        /// 成功发送的数据
        /// </summary>
      public   struct SendCompleteinfos
        {
           public   byte[] senddata;
           public  Converts.DeviceOper CD;
        }
      
        private void SAEA_Completed(object sender, SocketAsyncEventArgs e)
        {
            RegTime = DateTime.Now;
            if (e.LastOperation != SocketAsyncOperation.Send )
                return;
            SendCompleteinfos sc = new SendCompleteinfos();
            sc = (SendCompleteinfos)e.UserToken;
            //string print = string.Format("{0}--向{1}发送数据：{2}", DateTime.Now.ToString(), Uid, Converts.byteToHexStr(sc.senddata));         
            string print = string.Format("{0}--向{1}发送数据({3}) => {2}", DateTime.Now.ToString(), Uid, RegDevice.SendString(Uid, sc.senddata), sc.senddata.Length );
            if (sc.CD == Converts.DeviceOper.Register)
            {
                Console.WriteLine(print);
                WriteLog(Uid, "", 4, print);
            }
            else 
            {
                Console.WriteLine(print);
                WriteLog(Uid, "", 2, print);
            }
            e.Dispose();
        }

        /// <summary>
        /// 保存套接字，用于单个模块收发
        /// </summary>
        /// <param name="S"></param>
        public void SaveSocket(Socket S)
        {
            ReceiveSAEA.AcceptSocket  = S;
            RegTime = DateTime.Now;   //收到数据更新注册时间
            LoginTime = DateTime.Now; //收到数据更新登陆时间
            IPEndPoint ip = (IPEndPoint)S.RemoteEndPoint;
            LoginIP = ip.Address.ToString() + ":" + ip.Port.ToString(); //获取连接IP地址
        }

        /// <summary>
        /// 设备编号，“-1”表示不可用
        /// </summary>
        private string _Uid = "-1";
        public string Uid
        {
            set
            {
                _Uid = value;
                NotifyPropertyChanges("Uid");
            }
            get
            {
                return _Uid;
            }
        }


        /// <summary>
        /// 设备登陆时间
        /// </summary>
        public DateTime LoginTime { set; get; }


        private DateTime _RegTime;
        /// <summary>
        /// 设备最后一次心跳包时间
        /// </summary>
        public DateTime RegTime
        {
            set
            {
                _RegTime = value;
                NotifyPropertyChanges("RegTime");
            }
            get
            {
                return _RegTime;
            }
        }

        /// <summary>
        /// 是否是中心，TRUE是，False不是
        /// </summary>
        public bool IsCenter { set; get; }


        private string _DeviceGroup;
        /// <summary>
        /// 设备组号
        /// </summary>
        public string DeviceGroup
        {
            set
            {
                _DeviceGroup = value;
                Devicename = _DeviceGroup;
            }
            get { return _DeviceGroup; } }


        /// <summary>
        /// 接收缓冲区
        /// </summary>
        public byte[] ReceiveBuffer;

        /// <summary>
        /// 发送缓冲区
        /// </summary>
        public byte[] SendBuffer;

        /// <summary>
        /// 手机号
        /// </summary>
        public string MobileNo { set; get; }

        /// <summary>
        /// 连接设备的IP地址与端口号，格式 IP:PORT
        /// </summary>
        public string LoginIP { set; get; }


        /// <summary>
        /// 设备名称（组号名称）
        /// </summary>
        public string Devicename { set; get; }


        /// <summary>
        /// 一段时间没有收到注册信息或数据认为该设备掉线，需要清除,默认超时时间60S
        /// </summary>
        private int _RegTimeOut = 5;
        private  int RegTimeOut
        {
            set
            {
                _RegTimeOut = value;
            }
            get { return _RegTimeOut; }
        }

  

        /// <summary>
        /// 接收到数据事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ReceiveSAEA_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessReceive(e);
        }

        /// <summary>
        /// 接收数据处理
        /// </summary>
        /// <param name="e"></param>
        public void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                if (e.LastOperation != System.Net.Sockets.SocketAsyncOperation.Receive)
                    return;
                if (e.BytesTransferred == 0)    //这是一个断开连接的返回
                {
                    string print = string.Format("{0}--{1}连接断开", DateTime.Now.ToString(), Uid);
                    Console.WriteLine(print);
                    WriteLog(Uid, "", 3, print);
                    try
                    {
                        if(e.AcceptSocket.Connected )
                        e.AcceptSocket.Shutdown(SocketShutdown.Both);
                    }
                    catch (Exception ex)
                    {
                        logexception.WriteLogToFile(ex);

                    }
                    e.AcceptSocket.Close();
                    CloseOne(this);             //通过委托关闭连接
                    return;
                }
                 DataAnaly(e);  //收到数据处理  
                if (!ReceiveSAEA.AcceptSocket.Connected)     //连接被关闭不再打开接收
                    return;
                bool willRaiseEvent = (e.AcceptSocket.ReceiveAsync(ReceiveSAEA));  //开启下一次异步接收
                if (!willRaiseEvent)
                {
                    ProcessReceive(e);
                }
            }
            catch (Exception ex)
            {
                logexception.WriteLogToFile(ex);
                string print = string.Format("{0}--SocketModelProcessReceive错误:{1}", DateTime.Now.ToString(), ex.Message.ToString());
                Console.WriteLine(print);
                WriteLog("SocketModelProcessReceive", "", 3, print);
                try
                {
                    bool willRaiseEvent = (e.AcceptSocket.ReceiveAsync(ReceiveSAEA));  //开启下一次异步接收
                    if (!willRaiseEvent)
                    {
                        ProcessReceive(e);
                    }
                }
                catch (Exception e1)
                {
                    logexception.WriteLogToFile(e1);
                }
            }
        }


        /// <summary>
        /// 数据分析
        /// </summary>
        /// <param name="ReceiveData"></param>
        private void DataAnaly(object  obje)
        {
            SocketAsyncEventArgs e = (SocketAsyncEventArgs)obje;
            RegTime = DateTime.Now;   //收到数据更新注册时间
            byte[] ReceiveData = new byte[e.BytesTransferred];
            try
            {
                string print;
                Array.Copy(e.Buffer, ReceiveData, e.BytesTransferred);
                if(Uid=="-1")
                print = string.Format("{0}--从{1}收到数据({3}) <= {2}", DateTime.Now.ToString(), Uid, Converts.byteToHexStr(ReceiveData), ReceiveData.Length );
                else 
                 print = string.Format("{0}--从{1}收到数据({3}) <= {2}", DateTime.Now.ToString(), Uid, RegDevice.SendString(Uid,ReceiveData ), ReceiveData.Length );
                Console.WriteLine(print);
                if (ReceiveData.Length > 1)
                {
                    WriteLog(Uid, "", 1, print);
                }
                else
                {
                    WriteLog(Uid, "", 5, print); //心跳包或乱数据
                }
                if (Uid == "-1")  //未注册，如果有注册外数据将被忽略（必须注册完毕才能使用）
                {
                    if (DeviceLogin(ReceiveData))
                    {
                        RegisterMe(this);     //注册成功
                        TransferData(this);   //这是一条转发指令进行数据转发
                        Sendinfos.Clear();     //清除要发送的数据
                        return;
                    }
                    else
                    {
                        TransferData(this);   //这是一条转发指令进行数据转发
                        Sendinfos.Clear();     //清除要发送的数据
                        return;
                    }
                }
                else   //已注册，看是否符合转发规则，符合就转发
                {
                    Transfers(ReceiveData);
                }
            }
            catch (Exception ex)
            {
                logexception.WriteLogToFile(ex);
                string msg = ex.Message;
                string print = string.Format("{0}--从{1}收到数据解析失败({3}) <= {2}", DateTime.Now.ToString(), Uid, Converts.byteToHexStr(ReceiveData), ReceiveData.Length );
                Console.WriteLine(print);
                WriteLog(Uid, "",1, print);
            }
           
        }

        public List<Device.DeviceModel.SendMsg> Sendinfos = new List<Device.DeviceModel.SendMsg>();

        /// <summary>
        /// 数据传输
        /// </summary>
        /// <param name="Data"></param>
        private void Transfers(byte[] Data)
        {
            bool IsADDfinish = true;        //添加转发数据是否完成
            if (RegDevice.IsHeartPack(Data))    //心跳包不做任何处理
                return;
            byte[] ReceiveData = RegDevice.ReceiveDataConvert(Data);   //数据转义
            if (IsCenter)  //中心过来数据需要判断下，节点来的数据直接转发中心
            {
                IsADDfinish = false;
                if (!RegDevice.TransferRule(ReceiveData, out Sendinfos))  //进入转发规则，符合规定的数据放在Sendinfo中,不符合查找下一个设备驱动
                {
                    string print = string.Format("{0}--{1}无转发对象({3}) X= {2}", DateTime.Now.ToString(), Uid, Converts.byteToHexStr(ReceiveData), ReceiveData.Length );
                    Console.WriteLine(print);
                    WriteLog(Uid, "", 6, print);
                    return;
                }
                IsADDfinish = true;
            }
            else
                {
                Device.DeviceModel.SendMsg[] DDS = new Device.DeviceModel.SendMsg[] {
                new Device.DeviceModel.SendMsg { TargetUid=Uid,SendBuffer=ReceiveData,DeviceOper= Converts.DeviceOper.Transfer  } };
                IsADDfinish = false;
                Sendinfos.AddRange (DDS);
                IsADDfinish = true;
      
            }
            TransferData(this);   //如果有注册返回，需要发送
                while (!IsADDfinish) 
                    System.Threading.Thread.Sleep(1);       //正在添加不允许清空
                Sendinfos.Clear();     //清除要发送的数据
            
        }

        /// <summary>
        /// 需要传入的一些参数
        /// </summary>
        Dictionary<string, object> Params = new Dictionary<string, object>();

        /// <summary>
        /// 增加数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        void AddParams(string key,object value)
        {
            if (Params.ContainsKey(key))
                Params[key] = value;
            else
                Params.Add(key, value);
        }

        public delegate void Register(SocketModel S);    //注册成功
        Register RegisterMe;

        public  delegate  void   TransferDatas(SocketModel SourceData);  //数据转发
         TransferDatas TransferData;

        /// <summary>
        /// 设备注册
        /// </summary>
         /// <param name="Data"></param>
        private bool   DeviceLogin(byte[] Data)
        {      
            foreach (Device.IDevice DeviceOper in Devices)
            {
                byte[] rec = DeviceOper.ReceiveDataConvert(Data);     //数据转换
                Device.DeviceModel DM = new Device.DeviceModel();
                if (DeviceOper.DeviceReg(rec, ref DM, out Sendinfos))
                {
                    string printstr = string.Format("{0}--{1}注册成功", DateTime.Now.ToString(), DM.Uid);
                    Console.WriteLine(printstr);
                    WriteLog(DM.Uid, "",0, printstr);
                    Uid = DM.Uid;
                    IsCenter = DM.IsCenter;
                    DeviceGroup = DM.DeviceGroup;
                    MobileNo = DM.MobileNo;
                    RegDevice = DeviceOper;   //注册成功。可以得知是什么设备，以后这个设备就用这个驱动处理数据
                    AddParams("IP", ReceiveSAEA.AcceptSocket.LocalEndPoint.ToString());
                    RegDevice.SetParars(Params);
                    return true ;     //符合其中一种注册协议的，注册成功
                }
            }
            return false;   //没有符合注册协议的退出，注册失败
        }

        public delegate void CloseClient(SocketModel S);  //关闭自己
       public  CloseClient CloseOne;

        public void NotifyPropertyChanges(string propertyname)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
        }

        public event PropertyChangedEventHandler PropertyChanged;


    }
}
